import { GameLogic } from "./Runtime/GameLogic"
import { IEnemy, IRect, IVec2 } from "./Interface/State"
import { toFixed } from "./common/Util"

export interface circle {
    p: IVec2,//原点
    r: number,//半径
}
export interface Bounds {
    x: number,
    y: number,
    w: number,
    h: number,
}

export default class QuadTree {

    //边界
    bounds: Bounds
    //四叉树最大深度
    maxDeep: number
    //单个树最大容量
    maxNum: number
    //当前深度
    deep: number
    //叶子节点
    leafs: any[]
    //树
    nodes: QuadTree[]

    constructor(bounds: Bounds, deep: number, maxDeep?: number, maxNum?: number) {
        this.bounds = bounds
        this.deep = deep
        this.maxDeep = maxDeep || 5
        this.maxNum = maxNum || 4
        this.nodes = []
        this.leafs = []
    }

    split() {

        const x = this.bounds.x
        const y = this.bounds.y
        const w = this.bounds.w / 2
        const h = this.bounds.h / 2
        const nextDeep = this.deep + 1

        this.nodes[0] = new QuadTree(
            {
                x: x + w,
                y: y + h,
                w: w,
                h: h,
            },
            nextDeep,
        )

        this.nodes[1] = new QuadTree(
            {
                x: x,
                y: y + h,
                w: w,
                h: h,
            },
            nextDeep,
        )

        this.nodes[2] = new QuadTree(
            {
                x: x,
                y: y,
                w: w,
                h: h,
            },
            nextDeep,
        )

        this.nodes[3] = new QuadTree(
            {
                x: x + w,
                y: y,
                w: w,
                h: h,
            },
            nextDeep,
        )

    }
    getIndex(rect: IRect): number[] {
        const mindX = toFixed(this.bounds.w / 2) + this.bounds.x
        const mindY = toFixed(this.bounds.h / 2) + this.bounds.y

        const maxX = rect.x + rect.width
        const maxY = rect.y + rect.height
        const minX = rect.x
        const minY = rect.y

        const inTop = maxY > mindY
        const inBottom = minY < mindY
        const inLeft = minX < mindX
        const inRight = maxX > mindX
        // const inTop = maxY > mindY && minY < mindY + this.bounds.h / 2
        // const inBottom = minY < mindY && maxY > mindY - this.bounds.h / 2
        // const inLeft = minX < mindX && maxX > mindX - this.bounds.w / 2
        // const inRight = maxX > mindX && minX < mindX + this.bounds.w / 2

        const indexs: number[] = []

        if (inRight && inTop) {
            indexs.push(0)
        }
        if (inLeft && inTop) {
            indexs.push(1)
        }
        if (inLeft && inBottom) {
            indexs.push(2)
        }
        if (inRight && inBottom) {
            indexs.push(3)
        }
        return indexs
    }

    retrieve(rect: IRect): any[] {

        const leafs: any[] = []
        if (this.leafs.length == 0 && this.nodes.length == 0) {
            return leafs
        }
        if (this.leafs.length) {
            leafs.push(...this.leafs)
        } else {
            const indexs = this.getIndex(rect)
            for (let i = 0; i < indexs.length; i++) {
                leafs.push(...this.nodes[indexs[i]].retrieve(rect))
            }
        }

        return leafs.filter((v, i) => leafs.indexOf(v) === i)
    }

    /** 检索发生碰撞的怪物 */
    retrieveByCircule(cir: circle): IEnemy[] {

        const leafs: any = []
        if (this.leafs.length == 0 && this.nodes.length == 0) {
            return leafs
        }
        if (this.leafs.length) {
            leafs.push(...this.leafs)
        } else {
            const indexs = this.getIndexByCircle(cir)
            for (let i = 0; i < indexs.length; i++) {
                leafs.push(...this.nodes[indexs[i]].retrieveByCircule(cir))
            }
        }

        return leafs.filter((v: any, i: number) => leafs.indexOf(v) === i)
    }

    getIndexByCircle(cir: circle): any {

        const mindX = toFixed(this.bounds.w / 2) + this.bounds.x
        const mindY = toFixed(this.bounds.h / 2) + this.bounds.y

        const rectX = this.bounds.x
        const rectY = this.bounds.y
        const rectWidth = this.bounds.w
        const rectHeight = this.bounds.h

        const circleX = cir.p.x
        const circleY = cir.p.y
        const circleR = cir.r

        const fun = (rectTop: number, rectBottom: number, rectLeft: number, rectRight: number) => {
            // 检查圆心是否在矩形之外
            if (
                circleX > rectRight + circleR ||
                circleX < rectLeft - circleR ||
                circleY > rectTop + circleR ||
                circleY < rectBottom - circleR
            ) {
                return false;
            }

            // 计算圆心到矩形的水平和垂直距离
            var deltaX = Math.max(rectLeft - circleX, 0, circleX - rectRight);
            var deltaY = Math.max(circleY - rectTop, 0, rectBottom - circleY);
            // 判断最短距离是否小于等于圆的半径
            return toFixed(deltaX * deltaX + deltaY * deltaY) <= toFixed(circleR * circleR);
        }

        const indexs: number[] = []

        if (fun(rectHeight + rectY, mindY, mindX, rectWidth + rectX)) {
            indexs.push(0)
        }
        if (fun(rectHeight + rectY, mindY, rectX, mindX)) {
            indexs.push(1)
        }
        if (fun(mindY, rectY, rectX, mindX)) {
            indexs.push(2)
        }
        if (fun(mindY, rectY, mindX, rectWidth + rectX)) {
            indexs.push(3)
        }
        return indexs
    }
    insert(leaf: IEnemy) {
        const { position: { x, y } } = leaf
        const size = GameLogic.Ins.getEntitySizeByType(leaf.type)
        let rect: IRect = { x, y, width: size.w, height: size.h }
        if (this.nodes.length) {
            const indexs = this.getIndex(rect)
            for (let i = 0; i < indexs.length; i++) {
                this.nodes[indexs[i]].insert(leaf)
            }
            return
        }

        this.leafs.push(leaf)

        if (this.leafs.length > this.maxNum && this.deep < this.maxDeep) {
            this.split()
            for (let i = 0; i < this.leafs.length; i++) {

                const { position: { x, y }, type } = this.leafs[i]
                const size = GameLogic.Ins.getEntitySizeByType(type)
                const rect: IRect = { x, y, width: size.w, height: size.h }
                const indexs = this.getIndex(rect)
                for (let j = 0; j < indexs.length; j++) {
                    this.nodes[indexs[j]].insert(this.leafs[i])
                }
            }
            this.leafs = []
        }
    }
    clear() {
        if (this.nodes.length) {
            for (let i = 0; i < this.nodes.length; i++) {
                this.nodes[i].clear()
            }
        }

        this.leafs = []
        this.nodes = []
    }

}